package utils

import (
	"crypto/hmac"
	"crypto/md5"
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"unicode"
	"unicode/utf8"

	"github.com/techoner/gophp/serialize"
	"math"
	"math/big"
	"math/rand"
	"net"
	"reflect"
	"strconv"
	"strings"
	"time"
)

func TruncateFloatTwoDecimals(f float64) float64 {
	if f <= 0 {
		return 0
	}
	var shift = int(math.Pow(10, float64(2)))
	return float64(int(f*float64(shift))) / float64(shift)
}

func TruncateToDecimalPlaces(num float64, places int) float64 {
	shift := math.Pow(10, float64(places))
	truncated := math.Trunc(num*shift) / shift
	return truncated
}

func TruncateFloatTwoOrFourDecimals(f float64) float64 {
	if math.Abs(f) < 1 {
		return TruncateToDecimalPlaces(f, 4)
	} else {
		return TruncateToDecimalPlaces(f, 2)
	}
}

func Struct2Map(obj interface{}) map[string]interface{} {
	t := reflect.TypeOf(obj)
	v := reflect.ValueOf(obj)

	var data = make(map[string]interface{})
	for i := 0; i < t.NumField(); i++ {
		data[t.Field(i).Name] = v.Field(i).Interface()
	}
	return data
}

// 生成不通类型的随机数
func RandStr(k string, n int) string {
	// r, pattern := "", ""
	length := 0
	pattern := ""
	switch k {
	case "n":
		pattern = "1234567890"
		length = 9
		break
	case "s":
		pattern = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLOMNOPQRSTUVWXYZ"
		length = 52
		break
	case "r":
		pattern = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLOMNOPQRSTUVWXYZ"
		length = 62
		break
	case "y":
		pattern = "1234567890abcdefghijklmnopqrstuvwxyz"
		length = 35
		break
	case "a":
		pattern = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLOMNOPQRSTUVWXYZ"
		length = 62
		break
	case "ms":
		pattern = "abcdefghijklmnopqrstuvwxyz"
		length = 25
		break

	default:
		pattern = "1234567890"
		length = 9
		break
	}
	str := ""
	for i := 0; i < n; i++ {
		//num := rand.Intn(len(pattern)-1)
		num := rand.Intn(length) //
		str = str + pattern[num:num+1]
	}
	return str
}

// 校验用户密码是否正确
func ChkPass(passwordDecode, password, mdToken string) bool {
	return MdDecode(passwordDecode, mdToken) == password
}

// 解密密码
func DePass(password, mdToken string) string {
	return MdDecode(password, mdToken)
}

// 获取加密后的密码
func GetEncodePass(password, token string) string {
	return MdEncode(password, token)
}

// 异或算法
func MdXor(str, key string) string {
	if str == "" || key == "" {
		return ""
	}
	//获取加密字符串的byte长度
	str_byte := []byte(str)
	str_lens := len(str_byte) //<-

	//获取key的字符串长度
	my_key := []byte(key)
	k_lens := len(my_key) //<-

	j := 0
	rt := []byte{}
	for i := 0; i < str_lens; i++ {
		j = i % k_lens
		rt = append(rt, str_byte[i]^my_key[j])
	}
	return string(rt)
}

// 梦蝶跨平台加密算法
func MdEncode(str string, key string) string {
	//本密钥有效期时间戳
	time_str := GetTime("13")
	//随机13位字符串
	rand_str13 := RandStr("r", 13)
	//base64转码待加密内容z
	str_byte := []byte(str)
	str_64 := base64.StdEncoding.EncodeToString(str_byte)

	//生成随机md5key
	rand_key_md5 := Md5(time_str + rand_str13 + key)

	//用随机字符加密,并且在尾部拼上异或的时间戳,格式为:解密内容+13位时间戳加密内容(随机13位字符串异或加密)+随机13位字符串(交换key异或加密)

	str_xor := MdXor(str_64, rand_key_md5) + MdXor(time_str, rand_str13) + MdXor(rand_str13, key)

	return base64.StdEncoding.EncodeToString([]byte(str_xor))
}

func GetTime(t string) string {
	now_time := time.Now().UnixNano()/1e6 + 120*1000
	if t == "13" {
		return strconv.FormatInt(now_time, 10)
	}
	now_time = time.Now().Unix()
	return strconv.FormatInt(now_time, 10)
}

func GetNowFormatTime() string {
	return time.Now().Format("2006-01-02 15:04:05")
}

// 截取字符串 start 起点下标 end 终点下标(不包括)
func Substr(str string, start int, end int) string {
	rs := []rune(str)
	length := len(rs)

	if start < 0 || start > length {
		return ""
		//panic("start is wrong")
	}

	if end < 0 || end > length {
		return ""
		//panic("end is wrong")
	}

	return string(rs[start:end])
}

// 梦蝶跨平台解密算法
func MdDecode(str string, key string) string {
	//找到空格替换为+号
	str = strings.Replace(str, " ", "+", -1)
	//base64解码待加密内容
	dec64_str, err := base64.StdEncoding.DecodeString(str)
	dec_str := string(dec64_str)

	if err != nil {
		fmt.Println(err)
	}
	//分离加密内容,随机密钥,随机字符串
	len_s := len(dec64_str)

	//13位字符串分离
	rand_str := MdXor(Substr(dec_str, len_s-13, len_s), key)
	//13位时间戳
	now_time := MdXor(Substr(dec_str, len_s-26, len_s-13), rand_str)

	//生成随机key
	rand_key_md5 := Md5(now_time + rand_str + key)

	//还原数据
	str_xor := MdXor(Substr(dec_str, 0, len_s-26), rand_key_md5)
	str_xor_b, _ := base64.StdEncoding.DecodeString(str_xor)
	return string(str_xor_b)
}

func Md6(password string) string {
	out, _ := json.Marshal(password)
	jsonbyte, _ := serialize.Marshal(string(out))
	return Md5(string(jsonbyte))
}

func Md5(s string) string {
	h := md5.New()
	h.Write([]byte(s))
	return hex.EncodeToString(h.Sum(nil))
}

func Md516(data string) string {
	return Md5(data)[8:24]
}

func CreateOrderId(prefix string) string {
	var nowTime int64 = time.Now().Unix()
	var s string = time.Unix(nowTime, 0).Format("20060102150405")
	return fmt.Sprintf("%s%s%s", prefix, s, RandStr("n", 17))
}

func GetZeroTimestamp() int64 {
	currentTime := time.Now()
	startTime := time.Date(currentTime.Year(), currentTime.Month(), currentTime.Day(), 0, 0, 0, 0, currentTime.Location()).Unix()
	return startTime
}

func Ucfirst(str string) string {
	for i, v := range str {
		return string(unicode.ToUpper(v)) + str[i+1:]
	}
	return ""
}

func IntIP(ip string) uint32 {
	ret := big.NewInt(0)
	ret.SetBytes(net.ParseIP(ip).To4())
	return uint32(ret.Uint64())
}

func IntToStr(intValue int64) string {
	return fmt.Sprintf("%d", intValue)
}

func ToJsonStr(entity interface{}) string {
	str, err := json.Marshal(entity)
	if err != nil {
		return ""
	}
	return string(str)
}

func Str2Int(num string, defaultNum int) (numInt int) {
	numInt, err := strconv.Atoi(num)
	if err != nil {
		return defaultNum
	}
	return
}

func OffsetV2(pageNum, pageSize int) (offset, pageSizeOut, pageNumOut int) {
	if pageNum <= 0 {
		pageNumOut = 1
	} else {
		pageNumOut = pageNum
	}

	if pageSize <= 0 {
		pageSizeOut = 10
	} else {
		pageSizeOut = pageSize
	}

	offset = (pageNumOut - 1) * pageSizeOut
	return offset, pageSizeOut, pageNumOut
}

func StrToInt(s string) (d int64, err error) {
	d, err = strconv.ParseInt(s, 10, 64)
	return
}

type Duplicates interface {
	~int | int64 | int32 | string
}

// HandleIntArrays 数组去重
func HandleIntArrays[T Duplicates](input []T) []T {
	unique := make(map[T]struct{})
	result := make([]T, 0, len(input))
	for _, item := range input {
		if _, exists := unique[item]; !exists {
			unique[item] = struct{}{}
			result = append(result, item)
		}
	}
	return result
}

func ArrStrToInt(input []string) []int64 {
	result := make([]int64, 0, len(input))
	for _, v := range input {
		if val, err := strconv.ParseInt(v, 10, 64); err == nil {
			result = append(result, val)
		}
	}
	return result
}

// BuildArrMap 将任意列表转换为 map，键为指定字段的值
func BuildArrMap[T any, K comparable](list []T, keyExtractor func(T) K) map[K]T {
	result := make(map[K]T, len(list))
	for _, item := range list {
		key := keyExtractor(item)
		result[key] = item
	}
	return result
}

// GetPastTimestamp 获取过去某个时间的时间戳
// 支持传入天数 (daysAgo) 或固定的类型 (year, month, day)。
// 支持设置特定时间的小时和分钟。
func GetPastTimestamp(daysAgo int, typeStr string, amount int, hour int, minute int) int64 {
	now := time.Now()
	var pastTime time.Time
	// 如果传递了天数，优先按照天数计算
	if daysAgo > 0 {
		pastTime = now.AddDate(0, 0, -daysAgo)
	} else {
		// 根据指定的类型和数量计算时间
		switch typeStr {
		case "year":
			pastTime = now.AddDate(-amount, 0, 0)
		case "month":
			pastTime = now.AddDate(0, -amount, 0)
		case "day":
			pastTime = now.AddDate(0, 0, -amount)
		default:
			// 如果 typeStr 不匹配，返回当前时间戳
			return now.Unix()
		}
	}

	// 设置特定的小时和分钟
	pastTime = time.Date(
		pastTime.Year(),
		pastTime.Month(),
		pastTime.Day(),
		hour,
		minute,
		0, // 秒
		0, // 纳秒
		pastTime.Location(),
	)

	return pastTime.Unix()
}

func HashKey(hashParam string, secretKey string) string {
	h := hmac.New(sha256.New, []byte(secretKey))
	if utf8.ValidString(hashParam) {
		h.Write([]byte(hashParam))
	} else {
		h.Write([]byte(hashParam))
	}

	return fmt.Sprintf("%x", h.Sum(nil))
}

func MkParams(params map[string]string) string {
	var sb strings.Builder
	first := true
	for k, v := range params {
		if !first {
			sb.WriteString("&")
		} else {
			first = false
		}
		sb.WriteString(k)
		sb.WriteString("=")
		sb.WriteString(v)
	}
	return sb.String()
}

// EscapeSQL 过滤非法字符
func EscapeSQL(s string) string {
	var sb strings.Builder
	for _, c := range s {
		switch c {
		case 0:
			sb.WriteString("\\0")
		case '\n':
			sb.WriteString("\\n")
		case '\r':
			sb.WriteString("\\r")
		case '\\':
			sb.WriteString("\\\\")
		case '\'':
			sb.WriteString("''")
		case '"':
			sb.WriteString("\\\"")
		case '\032':
			sb.WriteString("\\Z")
		default:
			sb.WriteRune(c)
		}
	}
	return sb.String()
}

func GetNowFive(y int) time.Time {
	now := time.Now()
	// 获取当前小时的第几个5分钟
	fiveMinIndex := now.Minute() / y

	currentHour := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, time.Local)
	targetTime := currentHour.Add(time.Duration(fiveMinIndex*y) * time.Minute)
	return targetTime
}

// SafeIndex 获取指定索引的元素，如果索引超出范围，则返回空字符串
func SafeIndex(slice []string, index int) string {
	if index >= 0 && index < len(slice) {
		return slice[index]
	}
	return ""
}

// GenerateRandomString 生成指定长度的随机字符串，字符集包含大小写字母和数字
func GenerateRandomString(length int) string {
	charset := "abcdefghijklmnopqrstuvwxyz0123456789"
	result := make([]byte, length)
	for i := range result {
		result[i] = charset[rand.Intn(len(charset))]
	}
	return string(result)
}

func GetAppDir() string {
	appDir, err := os.Getwd()
	if err != nil {
		file, _ := exec.LookPath(os.Args[0])
		applicationPath, _ := filepath.Abs(file)
		appDir, _ = filepath.Split(applicationPath)
	}
	return appDir
}

func GetCurrentPath() string {
	dir, _ := os.Executable()
	exPath := filepath.Dir(dir)
	return exPath
}

func ChunkArray[T any](arr []T, chunkSize int) [][]T {
	var chunks [][]T
	for i := 0; i < len(arr); i += chunkSize {
		end := i + chunkSize
		if end > len(arr) {
			end = len(arr)
		}
		chunks = append(chunks, arr[i:end])
	}
	return chunks
}

// RemoveSpaces 祛除函数空格
func RemoveSpaces(s string) string {
	return strings.ReplaceAll(s, " ", "")
}
